/********************************************************/
/*	Author:		gong_libin			*/
/*	Date:		2014_08_08			*/
/*	File:		GtRm2000.c			*/
/********************************************************/

#include "GtRm2000.h"
	
void* sg_pShm = NULL;

int main(int argc, char* argv[])
{
	int iReset = 0;
 	int iIndex = 0;
	int iReturn = 0;
	UINT uiThread = 1;
	GTRUCONF_S stConf;
	char* pszBlock = (char*)"HTTP";
	char* pszConf = (char*)"../conf/GtRm2000.conf";

	struct option stOptions[] = {
		{"conf", 1, 0, 'f'},
		{"block", 1, 0, 'b'},
		{"thread", 1, 0, 't'},
		{"reset", 0, 0, 'r'},
		{"version", 0, 0, 'v'},
		{"help", 0, 0, 'h'},
		{0, 0, 0, 0},
	};

	while (GT_FAILURE != (iReturn = getopt_long(argc, argv, "c:b:t:rvh", stOptions, &iIndex))) {
		switch (iReturn) {
			case 'c':
				pszConf = optarg;
				break;
			case 'b':
				pszBlock = optarg;
				break;
			case 't':
				uiThread = atoi(optarg);
				break;
			case 'r':
				iReset = 1;
				break;
			case 'v':
				GT_PRINT("GtStream Build on %s %s.\n", __DATE__, __TIME__);
				return GT_SUCCESS;
			case 'h':
				GT_PRINT("Usage: %s [options]\n", argv[0]);
				GT_PRINT("Valid options are:\n");
				GT_PRINT("[0] -c or --conf:       set configure\n");
				GT_PRINT("[0] -b or --block:      set config block\n");
				GT_PRINT("[0] -t or --thread:     set thread number\n");
				GT_PRINT("[0] -r or --reset:      reset share memory\n");
				GT_PRINT("[0] -v or --version:    show process version.\n");
				GT_PRINT("[0] -h or --help:       show help information.\n");
				GT_PRINT("GtStream Build on %s %s.\n", __DATE__, __TIME__);
				return GT_SUCCESS;
			default:
				return GT_SUCCESS;
		}
	}

	if (NULL != (sg_pShm = GtRuShmCreat(iReset))) {
		memset(&stConf, '\0', sizeof(GTRUCONF_S));
		if (GT_SUCCESS == GtRuConfigure(pszConf, pszBlock, &stConf)) {
			GtRuConfPrint(pszBlock, &stConf);
			GtR2Fork(uiThread, &stConf);
			sleep(GT_R2_TIM);
		}
		else {
			GT_ERROR("Failed to GtRuConfigure\n");
		}
	}
	else {
		GT_ERROR("Failed to GtRuShmCreat\n");
	}

	return GT_SUCCESS;
}

int GtR2Fork(UINT uiThread, GTRUCONF_S* pstConf)
{
	UINT uiCount = 0;
	char* pszReturn = NULL;
	char* pszEth[GT_RU_TTL] = { NULL };
	char* pszEther = pstConf->m_szEther;
	GTR2PARA_S stPara = {0, 0, pstConf};

	while (NULL != (pszReturn = (char*)strsep((char**)&pszEther, ";"))) pszEth[uiCount ++] = pszReturn;

	#ifndef _GT_DEBUG_
		for (uiCount = 0; uiCount < GT_RU_TTL; uiCount ++) {
			stPara.m_uiIndex = uiCount;
			if (NULL != pszEth[uiCount]) {
				switch (fork()) {
					case GT_SUCCESS:
						strcpy(pstConf->m_szEther, pszEth[uiCount]);
						GtR2Main(uiThread, &stPara);
						exit(GT_SUCCESS);
						break;
					case GT_FAILURE:
						GT_ERROR("%s\n", strerror(errno));
						return GT_FAILURE;
						break;
					default:
						break;
				}
			}
			else {
				break;
			}
		}
	#else
		strcpy(pstConf->m_szEther, pszEth[0]);
		GtR2Main(1, &stPara);
	#endif

	return GT_SUCCESS;
}

int GtR2Main(UINT uiThread, GTR2PARA_S* pstPara)
{
	signal(SIGINT, GtR2Quit);
	signal(SIGSEGV, GtR2Quit);
	signal(SIGTERM, GtR2Quit);

	GT_PRINT("Process[%d]: %d -> ", pstPara->m_uiIndex, getpid());
	GT_PRINT("%s is running...\n", pstPara->m_pstConf->m_szEther);

	if ((pstPara->m_iSocket = GtRuRawInitRecv(pstPara->m_pstConf->m_szEther)) > 0 ) {
		if (GT_FAILURE == GtR2Thread(uiThread, pstPara)) {
			GT_ERROR("Failed to GtR2Thread %s\n", pstPara->m_pstConf->m_szEther);
			return GT_FAILURE;
		}
	}
	else {
		GT_ERROR("Failed to GtRuRawInit %s\n", pstPara->m_pstConf->m_szEther);
		return GT_FAILURE;
	}

	return GT_SUCCESS;
}

void GtR2Quit(int iSignal)
{
	GT_ERROR("GtRm2000-1.2.0 stopped by signal %d\n", iSignal);

	exit(GT_FAILURE);
}

int GtR2Thread(UINT uiThread, GTR2PARA_S* pstPara)
{
	UINT uiCount = 0;
	pthread_t iThread = 0;
	int iReturn = GT_SUCCESS;

	while (uiCount < uiThread) {
		if (GT_SUCCESS != pthread_create(&iThread, NULL, GtR2Routine, pstPara)) {
			GT_ERROR("%s\n", strerror(errno));
			iReturn = GT_FAILURE;
			break;
		}
		uiCount += 1;
	}

	if (GT_SUCCESS == iReturn) {
		pthread_join(iThread, NULL);
	}

	return iReturn;
}

void* GtR2Routine(void* pPara)
{
	int iMod = 0;
	int iExp = 0;
	int iLength = 0;
	USHORT usPort = 0;
	struct udphdr* pstUdp = NULL;
	struct tcphdr* pstTcp = NULL;
	UCHAR uszBuffer[GT_KBYTES8] = { 0 };
	GTR2PARA_S* pstPara = (GTR2PARA_S*)pPara;
	GTRUCONF_S* pstConf = pstPara->m_pstConf;
	GTRUSHM_S* pstShm = (GTRUSHM_S*)sg_pShm + pstPara->m_uiIndex;

	struct ip* const c_pstIP = (struct ip*)uszBuffer;
	const USHORT c_usBase = atoi(((GTR2PARA_S*)pPara)->m_pstConf->m_szPort);

	#ifdef _GT_DEBUG_
		int iLog = 0;
		struct timeval stOn;
		struct timeval stOff;
		char szTim[GT_BYTE128] = { 0 };
		char szTmp[GT_BYTE128] = { 0 };
		char szLog[GT_KBYTES8] = { 0 };
	#endif

	while (1) {
		iMod = 0;
		iExp = 0;
		usPort = 0;
		pstUdp = NULL;
		pstTcp = NULL;
		#ifdef _GT_DEBUG_
			iLog = 0;
			memset(szTim, '\0', GT_BYTE128);
			memset(szTmp, '\0', GT_BYTE128);
			memset(szLog, '\0', GT_BYTE128);
		#endif

		if ((iLength = GtR2RawRecv(pstPara->m_iSocket, uszBuffer, GT_KBYTES8)) > 0) {
			#ifdef _GT_DEBUG_
				gettimeofday(&stOn, NULL);
				GT_R2_LOG("Process[%d -> %lu] start\n", getpid(), pthread_self());
			#endif

			/* ip */
			#ifdef _GT_DEBUG_
				GT_R2_LOG("SrcAddr: %s\t", (char*)inet_ntoa(c_pstIP->ip_src));
				GT_R2_LOG("DstAddr: %s\n", (char*)inet_ntoa(c_pstIP->ip_dst));
			#endif

			/* tcp | udp */
			switch (c_pstIP->ip_p) {
				case IPPROTO_TCP:
					pstTcp = (struct tcphdr*)((UCHAR*)c_pstIP + c_pstIP->ip_hl * 4);
					usPort = ntohs(pstTcp->th_dport);
					iExp = GT_R2_MIT;
					#ifdef _GT_DEBUG_
						GT_R2_LOG("Protocal: Tcp\t");
						GT_R2_LOG("SrcPort: %d\t", ntohs(pstTcp->th_sport));
						GT_R2_LOG("DstPort: %d\n", ntohs(pstTcp->th_dport));
					#endif
					break;
				case IPPROTO_UDP:
					pstUdp = (struct udphdr*)((UCHAR*)c_pstIP + c_pstIP->ip_hl * 4);
					usPort = ntohs(pstUdp->uh_dport);
					iExp = GT_R2_MIU;
					#ifdef _GT_DEBUG_
						GT_R2_LOG("Protocal: Udp\t");
						GT_R2_LOG("SrcPort: %d\t", ntohs(pstUdp->uh_sport));
						GT_R2_LOG("DstPort: %d\n", ntohs(pstUdp->uh_dport));
					#endif
					break;
				default:
					break;
			}

			/* route */
			if (c_usBase == usPort) {
				if (iLength > iExp) {
					__sync_add_and_fetch(&pstShm->m_ullRecv, 1);
					__sync_add_and_fetch(&pstShm->m_ullBytes, iLength);
					iMod = (UINT)htonl(c_pstIP->ip_src.s_addr) % pstConf->m_uiDevice;
					if (GtR2RawSend(GT_RU_MDSC, uszBuffer, iLength, GT_RU_MDAD, GT_RU_MDSZ) > 0) {
						__sync_add_and_fetch(&pstShm->m_ullSend, 1);
						#ifdef _GT_DEBUG_
							GT_R2_LOG("Mod[%d]: GtRuRawSendto [%d] success\n", iMod, iLength);
						#endif
					}
					else {
						#ifdef _GT_DEBUG_
							GT_R2_LOG("Mod[%d]: GtRuRawSendto [%d] failure\n", iMod, iLength);
						#endif
					}
				}
				else {
					#ifdef _GT_DEBUG_
						szLog[0] = '\0';
					#endif
				}
			}
			else {
				#ifdef _GT_DEBUG_
					szLog[0] = '\0';
				#endif
			}

			#ifdef _GT_DEBUG_
				if ('\0' != szLog[0]) {
					gettimeofday(&stOff, NULL);
					GtRuTimer(&stOn, &stOff, szTim);
					GT_R2_LOG("Process[%d -> %lu] over use time %s\n", getpid(), pthread_self(), szTim);
					GT_PRINT("%s\n", szLog);
				}
			#endif
		}
	}

	pthread_exit(NULL);
}
